﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="StsAdmin.cs" company="Collaboris Ltd.">
//   Copyright (c) Collaboris Ltd. All rights Reserved.
// </copyright>
// <summary>
//   Allows StsAdm Commands to be run from Saf
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Collaboris.Saf.Actions.Wss.Wrappers
{
    using System;
    using System.IO;

    using Entities;

    using Engine;

    using SharePoint.Wss.Attributes;

    [CollaborisClass(
        Title = "Run Stsadm commands",
        ReleaseStatus = ReleaseStatus.Stable,
        DisableDocumentation = false,
        ContactAuthorEmail = "Support@Collaboris.co.uk",
        Summary = "Allows a SQL Statement to be run against a database as specified " +
                  "by a supplied connection string.",
        ClassGuid = "{156E17A5-A7BA-4e1f-8A48-1BC341A7C666}",
        UndoImplemented = true
        )
    ]
    public class StsAdmin : WSSActionBase<WSSExternalEngineWrapper<StsAdminCommand>>
    {
        private StsAdminCommand currentCommand;

        /// <summary>
        /// Executes the Action using the values passed in by the Data Entity
        /// and the <see cref="SafContext"/>.
        /// </summary>
        /// <param name="context">A valid <see cref="SafContext"/> containing properties to run the Action with.</param>
        /// <param name="entity">A Valid <see cref="T"/> containing the parameters with which to run the Action with.</param>
        /// <returns>
        /// A <see cref="ProcessStatus"/> signifying the outcome of processing the Action.
        /// </returns>
        protected override ProcessStatus DoAction(SafContext context, WSSExternalEngineWrapper<StsAdminCommand> entity)
        {
            currentCommand = entity.DoScript;

            if (currentCommand == null)
                return ProcessStatus.Skip;

            return this.RunStsAdmin();
        }

        /// <summary>
        /// Undoes the action.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="entity">The entity.</param>
        /// <returns></returns>
        protected override ProcessStatus UndoAction(SafContext context, WSSExternalEngineWrapper<StsAdminCommand> entity)
        {
            currentCommand = entity.UndoScript;

            if (currentCommand == null)
                return ProcessStatus.Skip;

            return this.RunStsAdmin();
        }

        /// <summary>
        /// Checks the Data entity used by the Action for Validity
        /// </summary>
        /// <param name="context">The <see cref="SafContext"/> passed in by the SAF Engine.</param>
        /// <param name="entity">The data entity to Validate.</param>
        /// <param name="errors">List of <see cref="ValidationError"/>s</param>
        /// <param name="mode"><see cref="ProcessMode"/>signalling wheter we are Doing, or Undoing the action.</param>
        /// <returns>false if invalid; otherwise true.</returns>
        protected override bool IsValid(
            SafContext context,
            WSSExternalEngineWrapper<StsAdminCommand> entity,
            ref System.Collections.Generic.List<ValidationError> errors, 
            ProcessMode mode)
        {
            int currentCount = errors.Count;

            if (mode == ProcessMode.Do)
                this.currentCommand = entity.DoScript;
            else
                this.currentCommand = entity.UndoScript;

            if (this.currentCommand != null)
            {
                // Validate each object now.
                ValidateStringForNull(this.currentCommand.Operation, "StsAdminCommand.Operation", ref errors);

                if (this.currentCommand.Arguments != null)
                {
                    if (this.currentCommand.Arguments.Keys != null)
                    {
                        // loop through and validate all the keys and Values
                        foreach (string key in this.currentCommand.Arguments.Keys)
                        {
                            ValidateStringForNull(this.currentCommand.Arguments[key], "StsAdminCommand.Arguments[].Key", ref errors);
                        }
                    }
                }
            }

            // Have we added errors ?
            return errors.Count == currentCount;
        }

        /// <summary>
        /// Runs the STS admin command.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <returns>
        /// A <see cref="ProcessStatus"/> signifying the outcome of processing the Action.
        /// </returns>
        private ProcessStatus RunStsAdmin()
        {
            this.Trace.TraceMethodStart("RunStsAdm"); 
            string[] arguments = this.BuildArgsFromEntity(this.currentCommand);
            if (arguments.Length == 0)
            {
                Trace.TraceError("Unable to acquire arguments to pass into the StsAdmin Action - Action Skipped!");
                return ProcessStatus.Warning;
            }

            // now lets attempt to Go run the Action
            try
            {
                Trace.TraceInfo("Running StsAdm with these arguments : ", arguments);

                // Some STSADM operation wont have any arguments.
                if (this.currentCommand.Arguments != null)
                {
                    if (this.currentCommand.Arguments.Keys != null)
                    {
                        foreach (string key in this.currentCommand.Arguments.Keys)
                        {
                            this.Trace.TraceInfo("{0} - {1}", key, this.currentCommand.Arguments[key]);
                        }
                    }
                }

                using (StringWriter sw = new StringWriter())
                {
                    Console.SetOut(sw);

                    int rc = SharePoint.Wss.Utils.RunSTSAdmCommand(arguments);

                    Trace.TraceInfo(
                                "The following response was received from the StsAdm Command '{0}' - '{1}'",
                                this.currentCommand.Operation,
                                sw.ToString());

                    if (rc < 0)
                    {
                        throw new Exception(
                            String.Format("The StsAdm Command '{0}' returned '{1}'", this.currentCommand.Operation, rc));
                    }
                    
                }
            }
            catch (Exception ex)
            {
                this.Trace.TraceException("RunStsAdm", ex);
                throw;
            }
            finally
            {
                this.Trace.TraceMethodEnd("RunStsAdm");
            }
            
            this.Trace.TraceMethodEnd("RunStsAdm");
            return ProcessStatus.Success;
        }

        /// <summary>
        /// Builds the args from entity.
        /// </summary>
        /// <param name="entity">The entity.</param>
        /// <returns>a string array of the arguments</returns>
        private string[] BuildArgsFromEntity(StsAdminCommand entity)
        {
            string[] argsArray = new string[2];

            // add the StsAdm Operation in
            argsArray[0] = "-o";
            argsArray[1] = entity.Operation;

            // Someoperation (ex: execadmsvcjobs) doesn't have the arguments.
            if (entity.Arguments != null)
            {
                if (entity.Arguments.Keys != null)
                {
                    // now loop all the params and add them in too.
                    foreach (string key in entity.Arguments.Keys)
                    {
                        Array.Resize(ref argsArray, argsArray.Length + 2);
                        argsArray[argsArray.Length - 2] = "-" + key;
                        argsArray[argsArray.Length - 1] = entity.Arguments[key];
                    }
                }
            }

            return argsArray;
        }
    }
}
